﻿#include "ItemData.h"
#include "../Globals.h"

bool ItemInfo::isConsumable() const
{
    return Type == ItemType::Potion || Type == ItemType::Scroll || Type == ItemType::Food || Type == ItemType::Transform || Type == ItemType::Script;
}

bool ItemInfo::isFishingRod() const
{
    return Globals::FishingRodShapes.Contains(Shape);
}

std::string ItemInfo::getFriendlyName() const
{
    std::string temp = Name;
    temp = Regex::Replace(temp, R"(\d+$)", ""); //hides end numbers
    temp = Regex::Replace(temp, R"(\[[^]]*\])", ""); //hides square brackets

    return temp;
}

ItemInfo::ItemInfo()
{
    Stats = new Stats();
}

ItemInfo::ItemInfo(BinaryReader *reader, int version, int customVersion)
{
    Index = reader->ReadInt32();
    Name = reader->ReadString();
    Type = static_cast<ItemType>(reader->ReadByte());
    Grade = static_cast<ItemGrade>(reader->ReadByte());
    RequiredType = static_cast<RequiredType>(reader->ReadByte());
    RequiredClass = static_cast<RequiredClass>(reader->ReadByte());
    RequiredGender = static_cast<RequiredGender>(reader->ReadByte());
    Set = static_cast<ItemSet>(reader->ReadByte());

    Shape = reader->ReadInt16();
    Weight = reader->ReadByte();
    Light = reader->ReadByte();
    RequiredAmount = reader->ReadByte();

    Image = reader->ReadUInt16();
    Durability = reader->ReadUInt16();

    if (version <= 84)
    {
        StackSize = static_cast<unsigned short>(reader->ReadUInt32());
    }
    else
    {
        StackSize = reader->ReadUInt16();
    }

    Price = reader->ReadUInt32();

    if (version <= 84)
    {
        Stats = new Stats();
        Stats[Stat::MinAC] = reader->ReadByte();
        Stats[Stat::MaxAC] = reader->ReadByte();
        Stats[Stat::MinMAC] = reader->ReadByte();
        Stats[Stat::MaxMAC] = reader->ReadByte();
        Stats[Stat::MinDC] = reader->ReadByte();
        Stats[Stat::MaxDC] = reader->ReadByte();
        Stats[Stat::MinMC] = reader->ReadByte();
        Stats[Stat::MaxMC] = reader->ReadByte();
        Stats[Stat::MinSC] = reader->ReadByte();
        Stats[Stat::MaxSC] = reader->ReadByte();
        Stats[Stat::HP] = reader->ReadUInt16();
        Stats[Stat::MP] = reader->ReadUInt16();
        Stats[Stat::Accuracy] = reader->ReadByte();
        Stats[Stat::Agility] = reader->ReadByte();

        Stats[Stat::Luck] = reader->ReadSByte();
        Stats[Stat::AttackSpeed] = reader->ReadSByte();
    }

    StartItem = reader->ReadBoolean();

    if (version <= 84)
    {
        Stats[Stat::BagWeight] = reader->ReadByte();
        Stats[Stat::HandWeight] = reader->ReadByte();
        Stats[Stat::WearWeight] = reader->ReadByte();
    }

    Effect = reader->ReadByte();

    if (version <= 84)
    {
        Stats[Stat::Strong] = reader->ReadByte();
        Stats[Stat::MagicResist] = reader->ReadByte();
        Stats[Stat::PoisonResist] = reader->ReadByte();
        Stats[Stat::HealthRecovery] = reader->ReadByte();
        Stats[Stat::SpellRecovery] = reader->ReadByte();
        Stats[Stat::PoisonRecovery] = reader->ReadByte();
        Stats[Stat::HPRatePercent] = reader->ReadByte();
        Stats[Stat::MPRatePercent] = reader->ReadByte();
        Stats[Stat::CriticalRate] = reader->ReadByte();
        Stats[Stat::CriticalDamage] = reader->ReadByte();
    }


    unsigned char bools = reader->ReadByte();
    NeedIdentify = (bools & 0x01) == 0x01;
    ShowGroupPickup = (bools & 0x02) == 0x02;
    ClassBased = (bools & 0x04) == 0x04;
    LevelBased = (bools & 0x08) == 0x08;
    CanMine = (bools & 0x10) == 0x10;

    if (version >= 77)
    {
        GlobalDropNotify = (bools & 0x20) == 0x20;
    }

    if (version <= 84)
    {
        Stats[Stat::MaxACRatePercent] = reader->ReadByte();
        Stats[Stat::MaxMACRatePercent] = reader->ReadByte();
        Stats[Stat::Holy] = reader->ReadByte();
        Stats[Stat::Freezing] = reader->ReadByte();
        Stats[Stat::PoisonAttack] = reader->ReadByte();
    }

    Bind = static_cast<BindMode>(reader->ReadInt16());

    if (version <= 84)
    {
        Stats[Stat::Reflect] = reader->ReadByte();
        Stats[Stat::HPDrainRatePercent] = reader->ReadByte();
    }

    Unique = static_cast<SpecialItemMode>(reader->ReadInt16());
    RandomStatsId = reader->ReadByte();

    CanFastRun = reader->ReadBoolean();

    CanAwakening = reader->ReadBoolean();

    if (version > 83)
    {
        Slots = reader->ReadByte();
    }

    if (version > 84)
    {
        Stats = new Stats(reader);
    }

    bool isTooltip = reader->ReadBoolean();
    if (isTooltip)
    {
        ToolTip = reader->ReadString();
    }

    if (version < 70) //before db version 70 all specialitems had wedding rings disabled, after that it became a server option
    {
        if ((Type == ItemType::Ring) && (Unique != SpecialItemMode::None))
        {
            Bind |= BindMode::NoWeddingRing;
        }
    }
}

void ItemInfo::Save(BinaryWriter *writer)
{
    writer->Write(Index);
    writer->Write(Name);
    writer->Write(static_cast<unsigned char>(Type));
    writer->Write(static_cast<unsigned char>(Grade));
    writer->Write(static_cast<unsigned char>(RequiredType));
    writer->Write(static_cast<unsigned char>(RequiredClass));
    writer->Write(static_cast<unsigned char>(RequiredGender));
    writer->Write(static_cast<unsigned char>(Set));

    writer->Write(Shape);
    writer->Write(Weight);
    writer->Write(Light);
    writer->Write(RequiredAmount);

    writer->Write(Image);
    writer->Write(Durability);

    writer->Write(StackSize);
    writer->Write(Price);

    writer->Write(StartItem);

    writer->Write(Effect);

    unsigned char bools = 0;
    if (NeedIdentify)
    {
        bools |= 0x01;
    }
    if (ShowGroupPickup)
    {
        bools |= 0x02;
    }
    if (ClassBased)
    {
        bools |= 0x04;
    }
    if (LevelBased)
    {
        bools |= 0x08;
    }
    if (CanMine)
    {
        bools |= 0x10;
    }
    if (GlobalDropNotify)
    {
        bools |= 0x20;
    }
    writer->Write(bools);

    writer->Write(static_cast<short>(Bind));
    writer->Write(static_cast<short>(Unique));

    writer->Write(RandomStatsId);

    writer->Write(CanFastRun);
    writer->Write(CanAwakening);
    writer->Write(Slots);

    Stats->Save(writer);

    writer->Write(ToolTip != "");
    if (ToolTip != "")
    {
        writer->Write(ToolTip);
    }

}

ItemInfo *ItemInfo::FromText(const std::string &text)
{
    return nullptr;
}

std::string ItemInfo::ToText()
{
    return "";
}

std::string ItemInfo::ToString()
{
    return StringHelper::formatSimple("{0}: {1}", Index, Name);
}

bool UserItem::isAdded() const
{
    return AddedStats->getCount() > 0;
}

int UserItem::getWeight() const
{
    return Info->Type == ItemType::Amulet ? Info->Weight : Info->Weight * Count;
}

std::string UserItem::getFriendlyName() const
{
    return Count > 1 ? StringHelper::formatSimple("{0} ({1})", Info->getFriendlyName(), Count) : Info->getFriendlyName();
}

UserItem::UserItem(ItemInfo *info)
{
    SoulBoundId = -1;
    ItemIndex = info->Index;
    Info = info;
    AddedStats = new Stats();

    SetSlotSize();
}

UserItem::UserItem(BinaryReader *reader, int version, int customVersion)
{
    UniqueID = reader->ReadUInt64();
    ItemIndex = reader->ReadInt32();

    CurrentDura = reader->ReadUInt16();
    MaxDura = reader->ReadUInt16();

    if (version <= 84)
    {
        Count = static_cast<unsigned short>(reader->ReadUInt32());
    }
    else
    {
        Count = reader->ReadUInt16();
    }

    if (version <= 84)
    {
        AddedStats = new Stats();

        AddedStats[Stat::MaxAC] = reader->ReadByte();
        AddedStats[Stat::MaxMAC] = reader->ReadByte();
        AddedStats[Stat::MaxDC] = reader->ReadByte();
        AddedStats[Stat::MaxMC] = reader->ReadByte();
        AddedStats[Stat::MaxSC] = reader->ReadByte();

        AddedStats[Stat::Accuracy] = reader->ReadByte();
        AddedStats[Stat::Agility] = reader->ReadByte();
        AddedStats[Stat::HP] = reader->ReadByte();
        AddedStats[Stat::MP] = reader->ReadByte();

        AddedStats[Stat::AttackSpeed] = reader->ReadSByte();
        AddedStats[Stat::Luck] = reader->ReadSByte();
    }

    SoulBoundId = reader->ReadInt32();
    unsigned char Bools = reader->ReadByte();
    Identified = (Bools & 0x01) == 0x01;
    Cursed = (Bools & 0x02) == 0x02;

    if (version <= 84)
    {
        AddedStats[Stat::Strong] = reader->ReadByte();
        AddedStats[Stat::MagicResist] = reader->ReadByte();
        AddedStats[Stat::PoisonResist] = reader->ReadByte();
        AddedStats[Stat::HealthRecovery] = reader->ReadByte();
        AddedStats[Stat::SpellRecovery] = reader->ReadByte();
        AddedStats[Stat::PoisonRecovery] = reader->ReadByte();
        AddedStats[Stat::CriticalRate] = reader->ReadByte();
        AddedStats[Stat::CriticalDamage] = reader->ReadByte();
        AddedStats[Stat::Freezing] = reader->ReadByte();
        AddedStats[Stat::PoisonAttack] = reader->ReadByte();
    }

    int count = reader->ReadInt32();

    SetSlotSize(std::make_optional(count));

    for (int i = 0; i < count; i++)
    {
        if (reader->ReadBoolean())
        {
            continue;
        }
        UserItem *item = new UserItem(reader, version, customVersion);
        Slots[i] = item;

        delete item;
    }

    if (version <= 84)
    {
        GemCount = static_cast<unsigned short>(reader->ReadUInt32());
    }
    else
    {
        GemCount = reader->ReadUInt16();
    }

    if (version > 84)
    {
        AddedStats = new Stats(reader);
    }

    Awake = new Awake(reader);

    RefinedValue = static_cast<RefinedValue>(reader->ReadByte());
    RefineAdded = reader->ReadByte();

    if (version > 85)
    {
        RefineSuccessChance = reader->ReadInt32();
    }

    WeddingRing = reader->ReadInt32();

    if (version < 65)
    {
        return;
    }

    if (reader->ReadBoolean())
    {
        ExpireInfo = new ExpireInfo(reader, version, customVersion);
    }

    if (version < 76)
    {
        return;
    }

    if (reader->ReadBoolean())
    {
        RentalInformation = new RentalInformation(reader, version, customVersion);
    }

    if (version < 83)
    {
        return;
    }

    IsShopItem = reader->ReadBoolean();
}

void UserItem::Save(BinaryWriter *writer)
{
    writer->Write(UniqueID);
    writer->Write(ItemIndex);

    writer->Write(CurrentDura);
    writer->Write(MaxDura);

    writer->Write(Count);

    writer->Write(SoulBoundId);
    unsigned char Bools = 0;
    if (Identified)
    {
        Bools |= 0x01;
    }
    if (Cursed)
    {
        Bools |= 0x02;
    }
    writer->Write(Bools);

    writer->Write(Slots.size());
    for (int i = 0; i < Slots.size(); i++)
    {
        writer->Write(Slots[i] == nullptr);
        if (Slots[i] == nullptr)
        {
            continue;
        }

        Slots[i]->Save(writer);
    }

    writer->Write(GemCount);


    AddedStats->Save(writer);
    Awake->Save(writer);

    writer->Write(static_cast<unsigned char>(RefinedValue));
    writer->Write(RefineAdded);
    writer->Write(RefineSuccessChance);

    writer->Write(WeddingRing);

    writer->Write(ExpireInfo != nullptr);
    if (ExpireInfo != nullptr)
    {
        ExpireInfo->Save(writer);
    }

    writer->Write(RentalInformation != nullptr);
    if (RentalInformation != nullptr)
    {
        RentalInformation->Save(writer);
    }

    writer->Write(IsShopItem);
}

int UserItem::GetTotal(Stat type)
{
    return AddedStats[type] + Info->Stats[type];
}

unsigned int UserItem::Price()
{
    if (Info == nullptr)
    {
        return 0;
    }

    unsigned int p = Info->Price;


    if (Info->Durability > 0)
    {
        float r = ((Info->Price / 2.0F) / Info->Durability);

        p = static_cast<unsigned int>(MaxDura * r);

        if (MaxDura > 0)
        {
            r = CurrentDura / static_cast<float>(MaxDura);
        }
        else
        {
            r = 0;
        }

        p = static_cast<unsigned int>(std::floor(p / 2.0F + ((p / 2.0F) * r) + Info->Price / 2.0F));
    }


    p = static_cast<unsigned int>(p * (AddedStats->getCount() * 0.1F + 1.0F));


    return p * Count;
}

unsigned int UserItem::RepairPrice()
{
    if (Info == nullptr || Info->Durability == 0)
    {
        return 0;
    }

    auto p = Info->Price;

    if (Info->Durability > 0)
    {
        p = static_cast<unsigned int>(std::floor(MaxDura * ((Info->Price / 2.0F) / Info->Durability) + Info->Price / 2.0F));
        p = static_cast<unsigned int>(p * (AddedStats->getCount() * 0.1F + 1.0F));

    }

    auto cost = p * Count - Price();

    if (RentalInformation == nullptr)
    {
        return cost;
    }

    return cost * 2;
}

unsigned int UserItem::Quality()
{
    unsigned int q = static_cast<unsigned int>(AddedStats->getCount() + Awake->GetAwakeLevel() + 1);

    return q;
}

unsigned int UserItem::AwakeningPrice()
{
    if (Info == nullptr)
    {
        return 0;
    }

    unsigned int p = 1500;

    p = static_cast<unsigned int>((p * (1 + Awake->GetAwakeLevel() * 2)) * static_cast<unsigned int>(Info->Grade));

    return p;
}

unsigned int UserItem::DisassemblePrice()
{
    if (Info == nullptr)
    {
        return 0;
    }

    unsigned int p = 1500 * static_cast<unsigned int>(Info->Grade);

    p = static_cast<unsigned int>(p * ((AddedStats->getCount() + Awake->GetAwakeLevel()) * 0.1F + 1.0F));

    return p;
}

unsigned int UserItem::DowngradePrice()
{
    if (Info == nullptr)
    {
        return 0;
    }

    unsigned int p = 3000;

    p = static_cast<unsigned int>((p * (1 + (Awake->GetAwakeLevel() + 1) * 2)) * static_cast<unsigned int>(Info->Grade));

    return p;
}

unsigned int UserItem::ResetPrice()
{
    if (Info == nullptr)
    {
        return 0;
    }

    unsigned int p = 3000 * static_cast<unsigned int>(Info->Grade);

    p = static_cast<unsigned int>(p * (AddedStats->getCount() * 0.2F + 1.0F));

    return p;
}

void UserItem::SetSlotSize(std::optional<int> &size)
{
    if (!size)
    {
        switch (Info->Type)
        {
            case ItemType::Mount:
                if (Info->Shape < 7)
                {
                    size = std::make_optional(4);
                }
                else if (Info->Shape < 12)
                {
                    size = std::make_optional(5);
                }
                break;
            case ItemType::Weapon:
                if (Info->Shape == 49 || Info->Shape == 50)
                {
                    size = std::make_optional(5);
                }
                break;
        }
    }

    if (!size && Info == nullptr)
    {
        return;
    }
    if (size && size == Slots.size())
    {
        return;
    }
    if (!size && Info != nullptr && Info->Slots == Slots.size())
    {
        return;
    }

    Array::Resize(Slots, size ? size : Info->Slots);
}

unsigned short UserItem::getImage() const
{
    switch (Info->Type)
    {
        #pragma region Amulet and Poison Stack Image changes
        case ItemType::Amulet:
            if (Info->StackSize > 0)
            {
                switch (Info->Shape)
                {
                    case 0: //Amulet
                        if (Count >= 300)
                        {
                            return 3662;
                        }
                        if (Count >= 200)
                        {
                            return 3661;
                        }
                        if (Count >= 100)
                        {
                            return 3660;
                        }
                        return 3660;
                    case 1: //Grey Poison
                        if (Count >= 150)
                        {
                            return 3675;
                        }
                        if (Count >= 100)
                        {
                            return 2960;
                        }
                        if (Count >= 50)
                        {
                            return 3674;
                        }
                        return 3673;
                    case 2: //Yellow Poison
                        if (Count >= 150)
                        {
                            return 3672;
                        }
                        if (Count >= 100)
                        {
                            return 2961;
                        }
                        if (Count >= 50)
                        {
                            return 3671;
                        }
                        return 3670;
                }
            }
            break;
    }

    #pragma endregion

    return Info->Image;
}

UserItem *UserItem::Clone()
{
    UserItem *item = new UserItem(Info);
    item->UniqueID = UniqueID;
    item->CurrentDura = CurrentDura;
    item->MaxDura = MaxDura;
    item->Count = Count;
    item->GemCount = GemCount;
    item->DuraChanged = DuraChanged;
    item->SoulBoundId = SoulBoundId;
    item->Identified = Identified;
    item->Cursed = Cursed;
    item->Slots = Slots;
    item->AddedStats = new Stats(AddedStats);
    item->Awake = Awake;
    item->RefineAdded = RefineAdded;
    item->ExpireInfo = ExpireInfo;
    item->RentalInformation = RentalInformation;
    item->IsShopItem = IsShopItem;

//C# TO C++ CONVERTER TODO TASK: A 'delete item' statement was not added since item was used in a 'return' or 'throw' statement.
    return item;
}

ExpireInfo::ExpireInfo()
{
}

ExpireInfo::ExpireInfo(BinaryReader *reader, int version, int Customversion)
{
    ExpiryDate = DateTime::FromBinary(reader->ReadInt64());
}

void ExpireInfo::Save(BinaryWriter *writer)
{
    writer->Write(ExpiryDate.ToBinary());
}

RentalInformation::RentalInformation()
{
}

RentalInformation::RentalInformation(BinaryReader *reader, int version, int CustomVersion)
{
    OwnerName = reader->ReadString();
    BindingFlags = static_cast<BindMode>(reader->ReadInt16());
    ExpiryDate = DateTime::FromBinary(reader->ReadInt64());
    RentalLocked = reader->ReadBoolean();
}

void RentalInformation::Save(BinaryWriter *writer)
{
    writer->Write(OwnerName);
    writer->Write(static_cast<short>(BindingFlags));
    writer->Write(ExpiryDate.ToBinary());
    writer->Write(RentalLocked);
}

GameShopItem::GameShopItem()
{
}

GameShopItem::GameShopItem(BinaryReader *reader, int version, int Customversion)
{
    ItemIndex = reader->ReadInt32();
    GIndex = reader->ReadInt32();
    GoldPrice = reader->ReadUInt32();
    CreditPrice = reader->ReadUInt32();
    if (version <= 84)
    {
        Count = static_cast<unsigned short>(reader->ReadUInt32());
    }
    else
    {
        Count = reader->ReadUInt16();
    }
    Class = reader->ReadString();
    Category = reader->ReadString();
    Stock = reader->ReadInt32();
    iStock = reader->ReadBoolean();
    Deal = reader->ReadBoolean();
    TopItem = reader->ReadBoolean();
    Date = DateTime::FromBinary(reader->ReadInt64());
}

GameShopItem::GameShopItem(BinaryReader *reader, bool packet)
{
    ItemIndex = reader->ReadInt32();
    GIndex = reader->ReadInt32();
    Info = new ItemInfo(reader);
    GoldPrice = reader->ReadUInt32();
    CreditPrice = reader->ReadUInt32();
    Count = reader->ReadUInt16();
    Class = reader->ReadString();
    Category = reader->ReadString();
    Stock = reader->ReadInt32();
    iStock = reader->ReadBoolean();
    Deal = reader->ReadBoolean();
    TopItem = reader->ReadBoolean();
    Date = DateTime::FromBinary(reader->ReadInt64());
}

void GameShopItem::Save(BinaryWriter *writer, bool packet)
{
    writer->Write(ItemIndex);
    writer->Write(GIndex);
    if (packet)
    {
        Info->Save(writer);
    }
    writer->Write(GoldPrice);
    writer->Write(CreditPrice);
    writer->Write(Count);
    writer->Write(Class);
    writer->Write(Category);
    writer->Write(Stock);
    writer->Write(iStock);
    writer->Write(Deal);
    writer->Write(TopItem);
    writer->Write(Date.ToBinary());
}

std::string GameShopItem::ToString()
{
    return StringHelper::formatSimple("{0}: {1}", GIndex, Info->Name);
}

unsigned char Awake::AwakeSuccessRate = 70;
unsigned char Awake::AwakeHitRate = 70;
int Awake::MaxAwakeLevel = 5;
unsigned char Awake::Awake_WeaponRate = 1;
unsigned char Awake::Awake_HelmetRate = 1;
unsigned char Awake::Awake_ArmorRate = 5;
unsigned char Awake::AwakeChanceMin = 1;
std::vector<float> Awake::AwakeMaterialRate = {1.0F, 1.0F, 1.0F, 1.0F};
std::vector<unsigned char> Awake::AwakeChanceMax = {1, 2, 3, 4};
std::vector<std::vector<std::vector<unsigned char>>> Awake::AwakeMaterials;

Awake::Awake()
{
}

Awake::Awake(BinaryReader *reader)
{
    Type = static_cast<AwakeType>(reader->ReadByte());
    int count = reader->ReadInt32();
    for (int i = 0; i < count; i++)
    {
        listAwake.push_back(reader->ReadByte());
    }
}

void Awake::Save(BinaryWriter *writer)
{
    writer->Write(static_cast<unsigned char>(Type));
    writer->Write(listAwake.size());
    for (auto value : listAwake)
    {
        writer->Write(value);
    }
}

bool Awake::IsMaxLevel()
{
    return listAwake.size() == Awake::MaxAwakeLevel;
}

int Awake::GetAwakeLevel()
{
    return listAwake.size();
}

unsigned char Awake::GetAwakeValue()
{
    unsigned char total = 0;

    for (auto value : listAwake)
    {
        total += value;
    }

    return total;
}

bool Awake::CheckAwakening(UserItem *item, AwakeType type)
{
    if (item->Info->Bind::HasFlag(BindMode::DontUpgrade))
    {
        return false;
    }

    if (item->Info->CanAwakening != true)
    {
        return false;
    }

    if (item->Info->Grade == ItemGrade::None)
    {
        return false;
    }

    if (IsMaxLevel())
    {
        return false;
    }

    if (this->Type == AwakeType::None)
    {
        if (item->Info->Type == ItemType::Weapon)
        {
            if (type == AwakeType::DC || type == AwakeType::MC || type == AwakeType::SC)
            {
                this->Type = type;
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (item->Info->Type == ItemType::Helmet)
        {
            if (type == AwakeType::AC || type == AwakeType::MAC)
            {
                this->Type = type;
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (item->Info->Type == ItemType::Armour)
        {
            if (type == AwakeType::HPMP)
            {
                this->Type = type;
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    else
    {
        if (this->Type == type)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

int Awake::UpgradeAwake(UserItem *item, AwakeType type, std::vector<bool> &isHit)
{
    //return -1 condition error, -1 = dont upgrade, 0 = failed, 1 = Succeed,  
    isHit = std::vector<bool>();
    if (CheckAwakening(item, type) != true)
    {
        return -1;
    }

    Random *rand = new Random(DateTime::Now.Millisecond);

    if (rand->Next(0, 100) <= AwakeSuccessRate)
    {
        isHit = Awakening(item);

        delete rand;
        return 1;
    }
    else
    {
        isHit = MakeHit(1, _);

        delete rand;
        return 0;
    }

    delete rand;
}

int Awake::RemoveAwake()
{
    if (listAwake.size() > 0)
    {
        listAwake.Remove(listAwake[listAwake.size() - 1]);

        if (listAwake.empty())
        {
            Type = AwakeType::None;
        }

        return 1;
    }
    else
    {
        Type = AwakeType::None;
        return 0;
    }
}

int Awake::GetAwakeLevelValue(int i)
{
    return listAwake[i];
}

unsigned char Awake::GetDC()
{
    return (Type == AwakeType::DC ? GetAwakeValue() : static_cast<unsigned char>(0));
}

unsigned char Awake::GetMC()
{
    return (Type == AwakeType::MC ? GetAwakeValue() : static_cast<unsigned char>(0));
}

unsigned char Awake::GetSC()
{
    return (Type == AwakeType::SC ? GetAwakeValue() : static_cast<unsigned char>(0));
}

unsigned char Awake::GetAC()
{
    return (Type == AwakeType::AC ? GetAwakeValue() : static_cast<unsigned char>(0));
}

unsigned char Awake::GetMAC()
{
    return (Type == AwakeType::MAC ? GetAwakeValue() : static_cast<unsigned char>(0));
}

unsigned char Awake::GetHPMP()
{
    return (Type == AwakeType::HPMP ? GetAwakeValue() : static_cast<unsigned char>(0));
}

std::vector<bool> Awake::MakeHit(int maxValue, int &makeValue)
{
    float stepValue = static_cast<float>(maxValue) / 5.0f;
    float totalValue = 0.0f;
    std::vector<bool> isHit(5);
    Random *rand = new Random(DateTime::Now.Millisecond);

    for (int i = 0; i < 5; i++)
    {
        if (rand->Next(0, 100) < AwakeHitRate)
        {
            totalValue += stepValue;
            isHit[i] = true;
        }
        else
        {
            isHit[i] = false;
        }
    }

    makeValue = totalValue <= 1.0f ? 1 : static_cast<int>(totalValue);

    delete rand;
    return isHit;
}

std::vector<bool> Awake::Awakening(UserItem *item)
{
    int minValue = AwakeChanceMin;
    int maxValue = (AwakeChanceMax[static_cast<int>(item->Info->Grade) - 1] < minValue) ? minValue : AwakeChanceMax[static_cast<int>(item->Info->Grade) - 1];

    int result;
    std::vector<bool> returnValue = MakeHit(maxValue, result);

    switch (item->Info->Type)
    {
        case ItemType::Weapon:
            result *= static_cast<int>(Awake_WeaponRate);
            break;
        case ItemType::Armour:
            result *= static_cast<int>(Awake_ArmorRate);
            break;
        case ItemType::Helmet:
            result *= static_cast<int>(Awake_HelmetRate);
            break;
        default:
            result = 0;
            break;
    }

    listAwake.push_back(static_cast<unsigned char>(result));

    return returnValue;
}

ItemRentalInformation::ItemRentalInformation()
{
}

ItemRentalInformation::ItemRentalInformation(BinaryReader *reader)
{
    ItemId = reader->ReadUInt64();
    ItemName = reader->ReadString();
    RentingPlayerName = reader->ReadString();
    ItemReturnDate = DateTime::FromBinary(reader->ReadInt64());
}

void ItemRentalInformation::Save(BinaryWriter *writer)
{
    writer->Write(ItemId);
    writer->Write(ItemName);
    writer->Write(RentingPlayerName);
    writer->Write(ItemReturnDate.ToBinary());
}

unsigned char ItemSets::getAmount() const
{
    switch (Set)
    {
        case ItemSet::Mundane:
        case ItemSet::NokChi:
        case ItemSet::TaoProtect:
        case ItemSet::Whisker1:
        case ItemSet::Whisker2:
        case ItemSet::Whisker3:
        case ItemSet::Whisker4:
        case ItemSet::Whisker5:
            return 2;
        case ItemSet::RedOrchid:
        case ItemSet::RedFlower:
        case ItemSet::Smash:
        case ItemSet::HwanDevil:
        case ItemSet::Purity:
        case ItemSet::FiveString:
        case ItemSet::Bone:
        case ItemSet::Bug:
        case ItemSet::DarkGhost:
            return 3;
        case ItemSet::Recall:
            return 4;
        case ItemSet::Spirit:
        case ItemSet::WhiteGold:
        case ItemSet::WhiteGoldH:
        case ItemSet::RedJade:
        case ItemSet::RedJadeH:
        case ItemSet::Nephrite:
        case ItemSet::NephriteH:
        case ItemSet::Hyeolryong:
        case ItemSet::Monitor:
        case ItemSet::Oppressive:
        case ItemSet::Paeok:
        case ItemSet::Sulgwan:
        case ItemSet::BlueFrostH:
        case ItemSet::BlueFrost:
            return 5;
        default:
            return 0;
    }
}

bool ItemSets::getSetComplete() const
{
    return Count >= getAmount();
}

RandomItemStat::RandomItemStat(ItemType Type)
{
    switch (Type)
    {
        case ItemType::Weapon:
            SetWeapon();
            break;
        case ItemType::Armour:
            SetArmour();
            break;
        case ItemType::Helmet:
            SetHelmet();
            break;
        case ItemType::Belt:
        case ItemType::Boots:
            SetBeltBoots();
            break;
        case ItemType::Necklace:
            SetNecklace();
            break;
        case ItemType::Bracelet:
            SetBracelet();
            break;
        case ItemType::Ring:
            SetRing();
            break;
        case ItemType::Mount:
            SetMount();
            break;
    }
}

void RandomItemStat::SetWeapon()
{
    MaxDuraChance = 2;
    MaxDuraStatChance = 13;
    MaxDuraMaxStat = 13;

    MaxDcChance = 15;
    MaxDcStatChance = 15;
    MaxDcMaxStat = 13;

    MaxMcChance = 20;
    MaxMcStatChance = 15;
    MaxMcMaxStat = 13;

    MaxScChance = 20;
    MaxScStatChance = 15;
    MaxScMaxStat = 13;

    AttackSpeedChance = 60;
    AttackSpeedStatChance = 30;
    AttackSpeedMaxStat = 3;

    StrongChance = 24;
    StrongStatChance = 20;
    StrongMaxStat = 2;

    AccuracyChance = 30;
    AccuracyStatChance = 20;
    AccuracyMaxStat = 2;
}

void RandomItemStat::SetArmour()
{
    MaxDuraChance = 2;
    MaxDuraStatChance = 10;
    MaxDuraMaxStat = 3;

    MaxAcChance = 30;
    MaxAcStatChance = 15;
    MaxAcMaxStat = 7;

    MaxMacChance = 30;
    MaxMacStatChance = 15;
    MaxMacMaxStat = 7;

    MaxDcChance = 40;
    MaxDcStatChance = 20;
    MaxDcMaxStat = 7;

    MaxMcChance = 40;
    MaxMcStatChance = 20;
    MaxMcMaxStat = 7;

    MaxScChance = 40;
    MaxScStatChance = 20;
    MaxScMaxStat = 7;

}

void RandomItemStat::SetHelmet()
{
    MaxDuraChance = 2;
    MaxDuraStatChance = 10;
    MaxDuraMaxStat = 3;

    MaxAcChance = 30;
    MaxAcStatChance = 15;
    MaxAcMaxStat = 7;

    MaxMacChance = 30;
    MaxMacStatChance = 15;
    MaxMacMaxStat = 7;

    MaxDcChance = 40;
    MaxDcStatChance = 20;
    MaxDcMaxStat = 7;

    MaxMcChance = 40;
    MaxMcStatChance = 20;
    MaxMcMaxStat = 7;

    MaxScChance = 40;
    MaxScStatChance = 20;
    MaxScMaxStat = 7;
}

void RandomItemStat::SetBeltBoots()
{
    MaxDuraChance = 2;
    MaxDuraStatChance = 10;
    MaxDuraMaxStat = 3;

    MaxAcChance = 30;
    MaxAcStatChance = 30;
    MaxAcMaxStat = 3;

    MaxMacChance = 30;
    MaxMacStatChance = 30;
    MaxMacMaxStat = 3;

    MaxDcChance = 30;
    MaxDcStatChance = 30;
    MaxDcMaxStat = 3;

    MaxMcChance = 30;
    MaxMcStatChance = 30;
    MaxMcMaxStat = 3;

    MaxScChance = 30;
    MaxScStatChance = 30;
    MaxScMaxStat = 3;

    AgilityChance = 60;
    AgilityStatChance = 30;
    AgilityMaxStat = 3;
}

void RandomItemStat::SetNecklace()
{
    MaxDuraChance = 2;
    MaxDuraStatChance = 10;
    MaxDuraMaxStat = 3;

    MaxDcChance = 15;
    MaxDcStatChance = 30;
    MaxDcMaxStat = 7;

    MaxMcChance = 15;
    MaxMcStatChance = 30;
    MaxMcMaxStat = 7;

    MaxScChance = 15;
    MaxScStatChance = 30;
    MaxScMaxStat = 7;

    AccuracyChance = 60;
    AccuracyStatChance = 30;
    AccuracyMaxStat = 7;

    AgilityChance = 60;
    AgilityStatChance = 30;
    AgilityMaxStat = 7;
}

void RandomItemStat::SetBracelet()
{
    MaxDuraChance = 2;
    MaxDuraStatChance = 10;
    MaxDuraMaxStat = 3;

    MaxAcChance = 20;
    MaxAcStatChance = 30;
    MaxAcMaxStat = 6;

    MaxMacChance = 20;
    MaxMacStatChance = 30;
    MaxMacMaxStat = 6;

    MaxDcChance = 30;
    MaxDcStatChance = 30;
    MaxDcMaxStat = 6;

    MaxMcChance = 30;
    MaxMcStatChance = 30;
    MaxMcMaxStat = 6;

    MaxScChance = 30;
    MaxScStatChance = 30;
    MaxScMaxStat = 6;
}

void RandomItemStat::SetRing()
{
    MaxDuraChance = 2;
    MaxDuraStatChance = 10;
    MaxDuraMaxStat = 3;

    MaxAcChance = 25;
    MaxAcStatChance = 20;
    MaxAcMaxStat = 6;

    MaxMacChance = 25;
    MaxMacStatChance = 20;
    MaxMacMaxStat = 6;

    MaxDcChance = 15;
    MaxDcStatChance = 30;
    MaxDcMaxStat = 6;

    MaxMcChance = 15;
    MaxMcStatChance = 30;
    MaxMcMaxStat = 6;

    MaxScChance = 15;
    MaxScStatChance = 30;
    MaxScMaxStat = 6;
}

void RandomItemStat::SetMount()
{
    SetRing();
}

std::string ChatItem::getRegexInternalName() const
{
    return StringHelper::formatSimple("<{0}>", StringHelper::replace(StringHelper::replace(Title, "(", "\\("), ")", "\\)"));
}

std::string ChatItem::getInternalName() const
{
    return StringHelper::formatSimple("<{0}/{1}>", Title, UniqueID);
}

ChatItem::ChatItem()
{
}

ChatItem::ChatItem(BinaryReader *reader)
{
    UniqueID = reader->ReadUInt64();
    Title = reader->ReadString();
    Grid = static_cast<MirGridType>(reader->ReadByte());
}

void ChatItem::Save(BinaryWriter *writer)
{
    writer->Write(UniqueID);
    writer->Write(Title);
    writer->Write(static_cast<unsigned char>(Grid));
}
